﻿using Puzzle.Properties;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace Puzzle {
    // 游戏
    class Game: PictureCtrl.Callback {
        // 回调
        public interface Callback {
            void OnGameOver();  // 游戏结束事件
        }
        
        // 难度
        public enum Difficulty {
            EASY = 9,       // 简单
            MEDIUM = 25,    // 中等
            HARD = 36       // 困难
        }

        // 状态
        public enum Status {
            END,        // 已结束
            PLAYING     // 游戏中
        }

        public Status CurrentStatus { get; private set; }   // 状态
        private readonly Brush mBrush;                      // 画刷
        private readonly Callback mCallback;                // 回调
        private Difficulty mDifficulty;                     // 难度
        private readonly Graphics mDrawer;                  // 绘图者
        private Point mEmptyPoint;                          // 空点，即没有图的那个点
        private readonly Control mGameBoard;                // 游戏区域
        private readonly Image mImage;                      // 图片
        private bool mPicHasBorder;                        // 图片控件是否有边框标志
        private PictureCtrl[] mPictures;                    // 图片控件数组
        private readonly Random mRandom;                    // 随机数生成器

        /**
         * 构造方法。
         * @param callback  回调
         * @param drawer    绘图者
         * @param gameBoard 游戏区域
         */
        public Game(Callback callback, Graphics drawer, Control gameBoard) {
            // 初始化成员
            CurrentStatus = Status.END;
            mBrush = new SolidBrush(Const.EMPTY_AREA_COLOR);
            mCallback = callback;
            mDifficulty = Difficulty.EASY;
            mDrawer = drawer;
            mGameBoard = gameBoard;
            mImage = new Bitmap(Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT);
            mPicHasBorder = true;
            mRandom = new Random();

            // 设置图片
            this.SetPicture(Resources.pic);
        }

        /**
         * 创建所有图片控件。
         */
        private void CreatePictures() {
            int left, top;                                          // 图片控件位置
            int baseCount = (int)Math.Sqrt((double)mDifficulty);    // 一行（列）的图片控件数
            int size = Const.EMPTY_AREA_WIDTH / baseCount;          // 图片控件尺寸
            Rectangle rect;                                         // 图片控件位置、尺寸
            int j = 0;                                              // 换行标志，0为第一行，1为第二行，依次类推

            // 删除所有图片控件
            this.DeletePictures();

            // 创建所有图片控件
            mPictures = new PictureCtrl[(int)mDifficulty];
            for (int i = 0; i < (int)mDifficulty; i++) {
                if (i % baseCount == 0 && i != 0) { // 如果一行满了，换到下一列
                    j++;
                }

                // 计算左上角坐标
                left = Const.EMPTY_AREA_X + size * (i % baseCount);
                top = Const.EMPTY_AREA_Y + size * j;

                // 设置位置、尺寸
                rect = new Rectangle(left, top, size, size);

                // 创建图片控件
                mPictures[i] = new PictureCtrl(this, mPicHasBorder, mImage, mGameBoard, rect);
            }
        }
        
        /**
         * 获取/设置游戏难度。
         */
        public Difficulty CurrentDifficulty {
            get {
                return mDifficulty;
            }

            set {
                // 设置难度
                mDifficulty = value;

                // 重新创建所有图片
                this.CreatePictures();

                // 将状态设置为已结束
                CurrentStatus = Status.END;
            }
        }

        /**
         * 删除所有图片控件。
         * @param game  游戏
         */
        private void DeletePictures() {
            if (mPictures == null) {
                return;
            }

            foreach (PictureCtrl pictureCtrl in mPictures) {
                if (pictureCtrl == null) {
                    break;
                }
                pictureCtrl.Dispose();
            }
        }

        /**
         * 游戏结束。
         */
        private void GameOver() {
            // 将状态设置为已结束
            CurrentStatus = Status.END;

            // 发送通知
            mCallback.OnGameOver();
        }

        /**
         * 判断是否胜利。
         * @return 胜利则返回 true，否则返回 false
         */
        private bool IsWon() {
            bool result = false;
            for (int i = 0; i < (int)mDifficulty; i++) {
                result = mPictures[i].IsOnOriginalPosition;
                if (!result) {
                    break;
                }
            }
            return result;
        }

        /**
         * 移动图片控件。
         * @param pictureCtrl   图片控件
         */
        public void MovePicture(PictureCtrl pictureCtrl) {
            if (CurrentStatus != Status.PLAYING) {
                return;
            }

            Point position = new Point(pictureCtrl.Left, pictureCtrl.Top);

            // 如果要移动的图片控件与空点相邻，则移动
            if (position.Y == mEmptyPoint.Y && Math.Abs(position.X - mEmptyPoint.X) == pictureCtrl.Width || 
                position.X == mEmptyPoint.X && Math.Abs(position.Y - mEmptyPoint.Y) == pictureCtrl.Height) {
                pictureCtrl.Left = mEmptyPoint.X;
                pictureCtrl.Top = mEmptyPoint.Y;
                mEmptyPoint.X = position.X;
                mEmptyPoint.Y = position.Y;

                // 判断是否胜利
                if (this.IsWon()) {
                    this.GameOver();
                }
            }
        }

        /**
         * 获取/设置图片控件是否有边框。
         */
        public bool PictureHasBorder {
            get {
                return mPicHasBorder;
            }

            set {
                mPicHasBorder = value;

                // 设置所有图片控件的边框
                for (int i = 0; i < (int)mDifficulty; i++) {
                    mPictures[i].SetBorder(value);
                }
            }
        }

        /**
         * 设置图片。
         * @param image 图片
         */
        public void SetPicture(Image image) {
            // 将图片画到游戏区域上
            Graphics drawer = Graphics.FromImage(mImage);
            drawer.DrawImage(image, new Rectangle(0, 0, mImage.Width, mImage.Height), 
                new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
            mDrawer.DrawImage(mImage, 0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT);

            // 画中间的空白区域
            mDrawer.FillRectangle(mBrush, Const.EMPTY_AREA_X, Const.EMPTY_AREA_Y, Const.EMPTY_AREA_WIDTH, Const.EMPTY_AREA_WIDTH);
                
            // 通知游戏区域重绘
            mGameBoard.Invalidate();

            // 创建所有图片
            this.CreatePictures();

            // 将状态设置为已结束
            CurrentStatus = Status.END;
        }

        /**
         * 游戏开始。
         */
        public void Start() {
            // 重新创建所有图片
            if (CurrentStatus != Status.END) {
                this.CreatePictures();
            }

            // 重置成员变量
            int baseCount = (int)Math.Sqrt((double)mDifficulty);
            mEmptyPoint.X = Const.EMPTY_AREA_X + mPictures[0].Width * baseCount;
            mEmptyPoint.Y = Const.EMPTY_AREA_Y + mPictures[0].Height * (baseCount - 1);
            CurrentStatus = Status.PLAYING;

            // 右下角图片控件右移
            Point position = new Point(mPictures[(int)mDifficulty - 1].Left, mPictures[(int)mDifficulty - 1].Top);
            mPictures[(int)mDifficulty - 1].Left = mEmptyPoint.X;
            mPictures[(int)mDifficulty - 1].Top = mEmptyPoint.Y;
            mEmptyPoint = position;

            // 乱序其它图片控件的位置
            int index1, index2;
            for (int i = 0; i < (int)mDifficulty - 1; i++) {
                do {
                    index1 = mRandom.Next((int)(mDifficulty - 1));
                    index2 = mRandom.Next((int)(mDifficulty - 1));
                } while (index1 == index2);
                this.SwapPictures(index1, index2);
            }
        }

        /**
         * 交换两个图片控件的位置。
         * @param index1    图片控件1的索引
         * @param index2    图片控件2的索引
         */
        private void SwapPictures(int index1, int index2) {
            Point position1 = new Point(mPictures[index1].Left, mPictures[index1].Top);
            Point position2 = new Point(mPictures[index2].Left, mPictures[index2].Top);
            mPictures[index1].Left = position2.X;
            mPictures[index1].Top = position2.Y;
            mPictures[index2].Left = position1.X;
            mPictures[index2].Top = position1.Y;
        }
    }
}
